JBoss Community Archive (Read Only)

GateIn Portal 3.5

Navigation Controller

The navigation controller is a major enhancement of GateIn that has several goals:

Controller in Action

Controller

The WebAppController is the component of GateIn that processes HTTP invocations and transforms them into a portal request. It has been improved with the addition of a request mapping engine (controller) whose role is to make the HTTP request decouple and create a portal request. The mapping engine makes two essential tasks:

  • Create a Map<QualifiedName, String> from an incoming HTTP request.

  • Render a Map<QualifiedName, String> as an HTTP URL.
    The goal of the controller (mapping engine) is to decouple the request processed by GateIn from the incoming HTTP request. Indeed, a request contains data that determine how the request will be processed and such data can be encoded in various places in the request, such as the request path, or a query parameter. The controller allows GateIn to route a request according to a set of parameters (a map) instead of the servlet request.

The controller configuration is declarative in an .xml file named controller.xml, allowing easy reconfiguration of the routing table and it is processed into an internal data structure that is used to perform resolution (routing or rendering).

The controller data cannot be modified by using the portlet interface, but can be still changed at runtime by modifying in the controller.xml file, then calling the WebAppController.reloadConfiguration() method.

Building controller

The controller configuration that contains the routing rules is loaded from the controller.xml file retrieved in the GateIn configuration directory. Its location is determined by the gatein.controller.config property.

WebAppController loads and initializes the mapping engine.

<!-- conf/portal/controller-configuration.xml of portal.war -->
<component>
  <type>org.exoplatform.web.WebAppController</type>
    <init-params>
      <value-param>
        <name>controller.config</name>
        <value>${gatein.portal.controller.config}</value>
      </value-param>
    </init-params>
</component>

GateIn's extension project can define its own routing table, thanks to the extension mechanism.

The controller.xml file can be changed and reloaded at runtime. This helps the test of different configurations easily (configuration loading operations) and provides more insight into the routing engine (the findRoutes operation). See Rebuiding controller below for more details.

  • ReBuilding controller
    WebAppController is annotated with the @Managed annotations and is bound under the view=portal,service=controller JMX name and under the "portalcontroller" REST name.

It provides the following attributes and operations:

  • Attribute configurationPath: the "read-only" configuration path of the controller.xml file.

  • Operation loadConfiguration: load a new configuration file from a specified XML path.

  • Operation reloadConfiguration: reload the configuration file.

  • Operation findRoutes: route the request argument through the controller and returns a list of all parameter map resolutions. The argument is a request URI, such as /g/:platform:administrators/administration/registry. It returns a string representation (List<Map>) of the matched routes.

Controller Configuration (controller.xml)

Most of the controller configuration defines rules (Routing table - contains routes object) that will drive the resolution. Routes are processed during the controller initialization to give a tree of node.

  • Each node is related to its parent with a matching rule that can either be an exact string matching or a regular expression matching.

  • Each node is associated with a set of parameters.
    A parameter is defined by a qualified name and there are three kinds of parameters explained in the sections below.

Route parameters

Route parameters define a fixed value which is associated with a qualified name.

  • Routing: Route parameters which allow the controller to distinguish branches easily and route the request accordingly.

  • Rendering: The system will select a route to render an URL if all route parameters are always matched.

    <route path="/foo">
      <route-param qname="gtn:handler">
        <value>portal</value>
      </route-param>
    </route>

    This configuration matches the "/foo" request path to the map (gtn:handler=portal). Conversely, it renders the map (gtn:handler=portal) as the "/foo" URL. This example shows two concepts:

  • Exact path matching ("/foo").

  • Route parameters ("gtn:handler").

Path parameters - _Regular expression support _

Path parameters allow associating a portion of the request path with a parameter. Such parameter will match any non empty portions of text except the / character (that is the [^/]+ regular expression). Otherwise, they can be associated with a regular expression for matching specific patterns. Path parameters are mandatory for matching since they are a part of the request path, however it is allowed to write regular expression matching an empty value.

  • Routing: Route is accepted if the regular expression is matched.

  • Rendering: The system will select a route to render an URL if all route parameters are always matched.
    Encoding

Path parameters may contain the '/' character which is a reserved char for the URI path. This case is specially handled by the navigation controller by using a special character to replace the '/' literals. By default, the character is the colon (":") and can be changed to other possible values (see controller XML schema for possible values) to give a greater amount of flexibility.

This encoding is applied only when the encoding is performed for parameters having a mode set to the default-form value, for instance, it does not happen for navigation node URI (for which / are encoded literally). The separator escape char can still be used but under it is percent escaped form, so by default, a path parameter value containing the colon ":" would be encoded as %3A and conversely the %3A value will be decoded as the colon ":".

<route path="/{gtn:path}">
</route>

As a result, routing and rendering are as below:

Routing and Rendering
Path "/foo"      <--> the map (gtn:path=foo)

Path "/foo:bar"  <--> the map (gtn:path=foo/bar)

If the request path contains another "/" char, it will not work. The default encoding mode is default-form. In the example above, "/foo/bar" is not matched, so the system returns an empty parameter map.

However, this problem could be solved with the following configuration:

<route path="/{gtn:path}">
  <path-param encoding="preserve-path" qname="gtn:path">
    <pattern>.*</pattern>
  </path-param>
</route>
  • The ".*" declaration allows matching any char sequence.

  • The "preserve-path" encoding tells the engine that the "/" chars should be handled by the path parameter itself as they have a special meaning for the router. Without this special encoding, "/" would be rendered as the ":" character and conversely the ":" character would be matched as the "/" character.

Request parameters

Request parameters are matched from the request parameters (GET or POST). The match can be optional as their representation in the request allows it.

  • Routing:

    • Route is accepted when a required parameter is present and matched in the request.

    • Route is accepted when an optional parameter is absent or matched in the request.

  • Rendering:

    • For required parameters, the system will select a route to render an URL when the parameter is present and matched in the map.

    • For optional parameters, the system will select a route to render an URL when the parameter is absent or matched in the map.

      <route path="/">
        <request-param name="path" qname="gtn:path"/>
      </route>

      Request parameters are declared by a request-param element and will match any value by default. A request like "/?path=foo" is mapped to the map (gtn:path=foo). The name attribute of the request-param tag defines the request parameter value. This element accepts more configuration:

  • A value or a pattern element that is a child element used to match a constant or a pattern.

  • A control-mode attribute with the optional or required value indicates if matching is mandatory or not.

  • A value-mapping attribute with the possible values, such as canonical, never-empty, never-null can be used to filter values after matching is done. For instance, a parameter configured with value-mapping="never-empty" and matched with the empty string value will not put the empty string in the map.

Route precedence

The order of route declaration is important as it affects how rules are matched. Sometimes, the same request could be matched by several routes and the routing table is ambiguous.

<route path="/foo">
  <route-param qname="gtn:handler">
    <value>portal</value>
  </route-param>
</route>
<route path="/{gtn:path}">
  <path-param encoding="preserve-path" qname="gtn:path">
    <pattern>.*</pattern>
  </path-param>
</route>

In that case, the request path "/foo" will always be matched by the first rule before the second rule. This can be misleading since the map (gtn:path=foo) would be rendered as "/foo" and would not be matched by the first rule. Such ambiguity can happen, it can be desirable or not.

Route nesting

Route nesting is possible and often desirable as it helps to:

  • Factor common parameters in a common rule.

  • Perform more efficient matching as the match of the common rule is done once for all the sub routes.

    <route path="/foo">
      <route-param qname="gtn:handler">
        <value>portal</value>
      </route-param>
      <route path="/bar">
        <route-param qname="gtn:path">
          <value>bar</value>
        </route-param>
      </route>
      <route path="/juu">
        <route-param qname="gtn:path">
          <value>juu</value>
        </route-param>
      </route>
    </route>
  • The request path "/foo/bar" is mapped to the (gtn:handler=portal,gtn:path=bar) map.

  • The request path "/foo/juu" is mapped to the (gtn:handler=portal,gtn:path=juu) map.

  • The request path "/foo" is not mapped as non leaf routes do not perform matches.

Integrate to GateIn WebUI framework

Routing

GateIn defines a set of parameters in its routing table, for each client request, the mapping engine processes the request path and returns the defined parameters with their values as a Map<QualifiedName, String>.

gtn:handler

The gtn:handler name is one of the most important qualified names as it determines which handler will take care of the request processing just after the controller has determined the parameter map. The handler value is used to make a lookup in the handler map of the controller. The handler is a class that extends the WebRequestHandler class and implements the execute(ControllerContext) method. Several handlers are available by default:

Those qualified names drives a request for the portal handler. They are used to determine which site to show and which path to resolve against a navigation. For instance, the (gtn:sitetype=portal,gtn:sitename=classic,gtn:path=home) instruct the portal handler to show the home page of the classic portal site.

gtn:lang

This parameter shows which language used in the URL for the portal handler. This is a new feature offered, now language can be specified on URL. It means that users can bookmark that URL (with the information about language) or he can changed the language simply by modifying the URL address.

gtn:componentid / gtn:action / gtn:objectid

The webui parameters used by the portal handler for managing webui component URLs for portal applications (but not for portlet applications).

Rendering

The controller is designed to render a Map<QualifiedName, String> as an HTTP URL according to its routing table. However, to integrate it for easy usage in WebUI Framework of GateIn, you need some more components:

  • PortalURL

  • NodeURL

  • ComponentURL

  • Portlet URLs

  • Webui URLBuilder

  • Groovy Templates

PortalURL

PortalURL plays a similar role at the portal level. Its main role is to abstract the creation of an URL for a resource managed by the portal.

public abstract class PortalURL<R, U extends PortalURL<U>>
{
   ...
}

The PortalURL declaration may seem a bit strange at first sight with two generic types: U and R.

  • The R generic type represents the type of the resource managed by the portal.

  • The U generic type is also described as self bound generic type. This design pattern allows a class to return subtypes of itself in the class declaring the generic type. Java Enums are based on this principle (class Enum<E extends Enum<E>>).
    A portal URL has various methods but certainly the most important method is the toString() method that generates an URL targeting to the resource. The remaining methods are getter and setter used to mutate the URL configuration, those options will affect the URL representation when it is generated.

  • resource: The mandatory resource associated with the URL.

  • locale: The optional locale used in the URL allowing the creation of bookmarkable URL containing a language.

  • confirm: The optional confirmation message displayed by the portal in the context of the portal UI.

  • ajax: The ajax option allowing an ajax invocation of the URL.
    Obtaining a PortalURL

PortalURL objects are obtained from RequestContext instance, such as PortalRequestContext, or PortletRequestContext. Usually, those are obtained thanks to the getCurrentInstance method of the RequestContext class:

RequestContext ctx = RequestContext.getCurrentInstance();

PortalURL is created via the createURL method that takes an input as a resource type. The resource type is usually a constant and type-safe object that allows retrieving the PortalURL subclasses:

RequestContext ctx = RequestContext.getCurrentInstance();
PortalURL<R, U> url = ctx.createURL(type);

In reality, you will use a concrete type constant and have instead more concrete code like:

RequestContext ctx = RequestContext.getCurrentInstance();
NodeURL url = ctx.createURL(NodeURL.TYPE);

The NodeURL.TYPE is actually declared as new ResourceType<NavigationResource, NodeURL>() that can be described as a type-literal object emulated by a Java anonymous inner class. Such literal was introduced by Neil Gafter as Super Type Token and popularized by Google Guice as Type Literal. It is an interesting way to create a literal representing a kind of Java type.

NodeURL

The NodeURL class is one of the subclass of PortalURL that is specialized in navigation node resources:

public class NodeURL extends PortalURL<NavigationResource, NodeURL>
{
   ...
}

The NodeURL class does not carry any generic types of its super class, which means that a NodeURL is type-safe and you do not have to worry about generic types.

Using a NodeURL is pretty straightforward:

NodeURL url = RequestContext.getCurrentInstance().createURL(NodeURL.TYPE);
url.setResource(new NavigationResource("portal", "classic, "home"));
String s = url.toString();

The NodeURL subclass contains the specialized setter methods to make its usage even easier:

UserNode node = ...;
NodeURL url = RequestContext.getCurrentInstance().createURL(NodeURL.TYPE);
url.setNode(node);
String s = url.toString();

ComponentURL

The ComponentURL subclass is another specialization of PortalURL that allows the creation of WebUI components URLs. ComponentURL is commonly used to trigger WebUI events from client side:

<% def componentURL = uicomponent.event(...); /*or uicomponent.url(...) */ %>
<a href=$componentURL>Click me</a>

Normally, you should not have to deal with it as the WebUI framework has already an abstraction for managing URL known as URLBuilder. The URLBuilder implementation delegates URL creation to ComponentURL objects.

Portlet URLs

Portlet URLs API implementation delegates to the portal ComponentURL (via the portlet container SPI). It is possible to control the language in the URL from a PortletURL object by setting the gtn:lang property:

  • When the property value is set to a value returned by the Locale#toString() method for locale objects having a non null language value and a null variant value, the URL generated by the PortletURL#toString() method will contain the locale in the URL.

  • When the property value is set to an empty string, the generated URL will not contain a language. If the incoming URL was carrying a language, this language will be erased.

  • When the property value is not set, it will not affect the generated URL.

    PortletURL url = resp.createRenderURL();
    url.setProperty("gtn:lang", "fr");
    writer.print("<a href='" + url + "'>French</a>");

Webui URLBuilder

This internal API used to create URL works as usual and delegates to the PortletURL API when the framework is executed in a portlet, and delegates to a ComponentURL API when the framework is executed in the portal context. The API has been modified to take in account the language in URL with two properties on the builder:

  • locale: A locale for setting on the URL.

  • removeLocale: A boolean for removing the locale present on the URL.

Groovy Templates

In a Groovy template, the mechanism to create an URL is the same as the way of APIs above, however a splash of integration has been done to make creation of NodeURL simpler. A closure is bound under the nodeurl name and is available for invocation anytime. It will simply create a NodeURL object and return it:

UserNode node = ...;
NodeURL url = nodeurl();
url.setNode(node);
String s = url.toString();

The nodeurl closure is bound to Groovy template in WebuiBindingContext.

// Closure nodeurl()
put("nodeurl", new Closure(this)
{
  @Override
  public Object call(Object[] args)
  {
    return context.createURL(NodeURL.TYPE);
  }
});

Changes and migration from GateIn 3.1.x

The navigation controller implies a migration of the client code that is coupled to several internal APIs of GateIn. The major impact is related to anything dealing with URL:

  • Creation of an URL representing a resource managed by the portal: navigation node or UI component.

  • Using HTTP request related information.

Migration of navigation node URL

Using free form node

The previous code for creating navigation node was:

String uri = Util.getPortalRequestContext().getPortalURI() + "home";

The new code will look like:

PortalURL nodeURL = nodeurl();
NavigationResource resource = new NavigationResource(SiteType.PORTAL, pcontext.getPortalOwner(), "home");
String uri = nodeURL.setResource(resource).toString();

Using UserNode object

The previous code for creating navigation node was:

UserNode node = ...;
String uri = Util.getPortalRequestContext().getPortalURI() + node.getURI()";

The new code will look like:

UserNode node = ...;
PortalURL nodeURL = nodeurl();
String uri = nodeURL.setNode(node).toString();

Security changes

Security configuration needs to be changed to keep the flexibility added by the navigation controller. In particular, the authentication does not depend anymore on path specified in web.xml but relies on the security mandated by the underlying resource instead. Here are the noticeable changes for security:

  • Authentication is now triggered on the "/login" URL when it does not have a username or a password specified. Therefore, the URL /login?initialURI=/classic/home is (more or less) equivalent to /private/classic/home.

  • When a resource cannot be viewed due to security constraint.

    • If the user is not logged, the authentication will be triggered.

    • Otherwise, a special page (the usual one) will be displayed.

Default handler

Redirection to the default portal used to be done by the index.jsp JSP page. This is not the case anymore, the index.jsp file has been removed and the welcome file in web.xml was removed too. Instead a specific handler in the routing table has been configured, the sole role of this handler is to redirect the request to the default portal when no other request has been matched previously:

<controller>
  ...
  <route path="/">
    <route-param qname="gtn:handler">
      <value>default</value>
    </route-param>
  </route>
</controller>

Legacy handler

Legacy URLs, such as /public/... and /private/... are now emulated to determine the best resource with the same resolution algorithm, but instead of displaying the page, it will make an HTTP 302 redirection to the correct URL. This handler is present in the controller configuration. There is a noticeable difference between two routes.

  • The public redirection attempts to find a node with the legacy resolution algorithm without authentication, which means that secured nodes will not be resolved and the redirection of a secured node will likely redirect to another page. For instance, resolving the URL /public/classic/administration/registry path will likely resolve to another node if the user is not authenticated and is not in the platform administrator group.

  • The private redirection first performs an authentication before doing the redirection. In that case, the /private/classic/administration/registry path will be redirected to the /portal/groups/:platform:administrators/administration/registry page if the user has the sufficient security rights.

Static resource handler

The "/" mapping for the "default" servlet is now replaced by mapping for the org.exoplatform.portal.application.PortalController servlet. It means that you need a handler (org.exoplatform.portal.application.StaticResourceRequestHandler) to serve static resources like image, CSS or JavaScript files in portal.war. And it should be configured, and extended easily thanks to the controller.xml file. This file can be overridden and can be changed and reloaded at runtime (WebAppController is MBean with some operations, such as reloadConfiguration()).

Declare StaticResourceHandler in controller.xml.

<route path="/{gtn:path}">
  <route-param qname="gtn:handler">
    <value>staticResource</value>
  </route-param>
  <path-param encoding="preserve-path" qname="gtn:path">
    <pattern>.*\.(jpg|png|gif|ico|css)</pattern>
  </path-param>
</route>

And you do not need these kinds of the following mapping in web.xml in portal.war anymore.

<servlet-mapping>
  <servlet-name>default</servlet-name>
  <url-pattern>*.jpg</url-pattern>
</servlet-mapping>
...

portal.war's web.xml changes

Declare DoLoginServlet:

<servlet>
  <servlet-name>DoLoginServlet</servlet-name>
  <servlet-class>org.exoplatform.web.login.DoLoginServlet</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>DoLoginServlet</servlet-name>
  <url-pattern>/dologin</url-pattern>
</servlet-mapping>

Delare portal servlet as the default servlet:

<servlet-mapping>
   <servlet-name>portal</servlet-name>
   <url-pattern>/</url-pattern>
</servlet-mapping>

Some mapping declarations for portal servlet are unused, so you should remove them: _/private/* /public/* /admin/* /upload/* /download/*_

Add some security constraints.

<security-constraint>
  <web-resource-collection>
    <web-resource-name>user authentication</web-resource-name>
      <url-pattern>/dologin</url-pattern>
      <url-pattern>/standalone/*</url-pattern>
      <url-pattern>/groups/*</url-pattern>
      <url-pattern>/users/*</url-pattern>
...
  </web-resource-collection>
</security-constraint>

You can remove the index.jsp file, and its declaration in the web.xml file thanks to the default request handler:

<welcome-file-list>
  <welcome-file>/index.jsp</welcome-file>
</welcome-file-list>

Dashboard changes

There are several important changes you need to take into account:

  • Dashboard are now bound to a single URL (/users/root by default) and dashboard pages are leaf of this path.

  • Dashboard lifecycle can be decoupled (create or destroy) from the identity creation in a configurable manner in UserPortalConfigService and exposed in configuration.properties under gatein.portal.idm.createuserportal and gatein.portal.idm.destroyuserportal.

  • By default, dashboard are not created when a user is registered.

  • A dashboard is created when the user accesses his dashboard URL.

Remove unused files

  • portal-unavailable.jsp: This file is presented before if a user goes to a non-available portal. But now the server sends a 404 status code instead.

  • portal-warning.jsp: This file is not used in any places.

JBoss.org Content Archive (Read Only), exported from JBoss Community Documentation Editor at 2020-03-10 12:48:08 UTC, last content change 2012-10-09 14:59:09 UTC.